package cn.swust.designpatternlearningplatform.service.impl;

import cn.hutool.core.lang.Pair;
import cn.swust.designpatternlearningplatform.common.Result.PageInfo;
import cn.swust.designpatternlearningplatform.dao.*;
import cn.swust.designpatternlearningplatform.domain.bo.StudentTopicTypeBo;
import cn.swust.designpatternlearningplatform.domain.bo.TopicWrongWithKnowledgePointBo;
import cn.swust.designpatternlearningplatform.domain.dto.*;
import cn.swust.designpatternlearningplatform.domain.entity.*;
import cn.swust.designpatternlearningplatform.domain.enums.TopicTypeEnum;
import cn.swust.designpatternlearningplatform.domain.enums.TopicUpdateTypeEnum;
import cn.swust.designpatternlearningplatform.event.TopicWrongEvent;
import cn.swust.designpatternlearningplatform.service.TopicService;
import cn.swust.designpatternlearningplatform.service.TopicWrongService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import java.sql.Timestamp;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

@Slf4j
@Service
public class TopicWrongServiceImpl implements TopicWrongService {
    private TopicUpdateDao topicUpdateDao;
    private TopicKnowledgeCorrelationDao topicKnowledgeCorrelationDao;
    private TopicKnowledgePointDao topicKnowledgePointDao;
    private TopicService topicService;
    private DailyExerciseDao dailyExerciseDao;
    private TopicWrongDao topicWrongDao;

    /**
     * 获取用户的错题中与对应知识点关联的题目
     *
     * @param topicWrongWithKnowledgePointBo
     * @return java.util.List<cn.swust.designpatternlearningplatform.domain.entity.Topic>
     * @author Tang
     * @date 2023/4/20 21:50
     **/
    @Override
    public List<TopicWrongDto> getWrongTopicInKnowledgePoint(PageInfo<TopicWrongDto> pageInfo, TopicWrongWithKnowledgePointBo topicWrongWithKnowledgePointBo, StudentTopicTypeBo studentTopicTypeBo) {
        if (pageInfo == null) {
            return null;
        }
        //封装分页参数
        PageInfo<TopicDto> dtoPageInfo = new PageInfo<>();
        BeanUtils.copyProperties(pageInfo, dtoPageInfo);
        //获取学生的错题信息
        List<TopicWrongRecord> topicWrongRecords = topicWrongDao.selectTopicWrongRecordByPoint(studentTopicTypeBo.getStudentNumber(),
                topicWrongWithKnowledgePointBo.getPointId(), (studentTopicTypeBo.getCurrent() - 1) * studentTopicTypeBo.getSize(),
                studentTopicTypeBo.getSize());
        //获取错题id
        List<Integer> topicIdInStudentWrongTopic = topicWrongRecords.stream().map(TopicWrongRecord::getTopicId).collect(Collectors.toList());
        //批量查询题目
        List<TopicDto> byTopicIds = topicService.getTopicByTopicIds(topicIdInStudentWrongTopic);
        Map<Integer, TopicDto> topicDtoMap = byTopicIds.stream().filter(Objects::nonNull).collect(Collectors.toMap(TopicDto::getTopicId, i -> i));
        return topicWrongRecords.stream().map(i -> {
            Integer topicId = i.getTopicId();
            TopicDto topicDto = topicDtoMap.get(topicId);
            if (topicDto == null) {
                return null;
            }
            TopicWrongDto topicWrongDto = new TopicWrongDto();
            BeanUtils.copyProperties(topicDto, topicWrongDto);
            BeanUtils.copyProperties(i, topicWrongDto);
            //设置题目类型
            topicWrongDto.setTopicType(TopicTypeEnum.topicTypeId(i.getTopicType()));
            return topicWrongDto;
        }).filter(Objects::nonNull).collect(Collectors.toList());
    }


    /**
     * 返回用户错题的总数量
     *
     * @param studentTopicTypeBo
     * @return java.lang.Integer
     * @author Tang
     * @date 2023/4/21 17:17
     **/
    @Override
    public Integer totalTopicWrong(StudentTopicTypeBo studentTopicTypeBo) {
        TopicUpdate topicUpdate = new TopicUpdate();
        topicUpdate.setStudentNumber(studentTopicTypeBo.getStudentNumber());
        topicUpdate.setUpdateType(TopicUpdateTypeEnum.WRONG.getTypeName());
        return topicUpdateDao.countTopicUpdateByStudent(topicUpdate);
    }

    /**
     * 获取用户在某知识点下的与知识点相关联的题目id
     *
     * @param topicWrongWithKnowledgePointBo
     * @return java.util.List<java.lang.Integer>
     * @author Tang
     * @date 2023/4/23 20:30
     **/
    @Override
    public Integer totalTopicWrongIdUnderPoint(TopicWrongWithKnowledgePointBo topicWrongWithKnowledgePointBo, StudentTopicTypeBo studentTopicTypeBo) {
        //该知识点下面所有的子知识点并查出与知识点相连的题目id
        TopicWrongPointLink wrongPointLink = topicUpdateDao.selectWrongLinkNum(studentTopicTypeBo.getStudentNumber(), topicWrongWithKnowledgePointBo.getPointId());
        if (wrongPointLink == null) {
            return 0;
        }
        return wrongPointLink.getNum();
    }

    @Override
    public List<TopicWrongPointLinkDto> getTopicPointLinkDto(String studentNumber, Integer current, Integer size) {
        return topicUpdateDao.selectTopicWrongPointLink(studentNumber, (current - 1) * size, size).stream().parallel().map(i -> {
            TopicWrongPointLinkDto topicWrongPointLinkDto = new TopicWrongPointLinkDto();
            Integer pointId = i.getPointId();
            String pointContent = topicKnowledgePointDao.selectKnowledgePointById(pointId).getPointContent();
            topicWrongPointLinkDto.setPointName(pointContent);
            topicWrongPointLinkDto.setPointId(pointId);
            topicWrongPointLinkDto.setNum(i.getNum());
            return topicWrongPointLinkDto;
        }).collect(Collectors.toList());
    }

    /**
     * 获取某知识点下所有的子id
     *
     * @param pointId
     * @return java.util.List<java.lang.Integer>
     * @author Tang
     * @date 2023/4/23 20:32
     **/
    private Collection<Integer> getTopicIdOfAllSonIds(Integer pointId) {
        List<Integer> allSonIds = new ArrayList<>();
        allSonIds.add(pointId);
        for (int i = 0; i < allSonIds.size(); i++) {
            topicKnowledgePointDao.selectKnowledgePointSonId(allSonIds.get(i)).forEach(j -> {
                if (j.getPointId() != null) {
                    allSonIds.add(j.getPointId());
                }
            });
        }
        //与知识点相连的题目id
        Collection<Integer> pointLinkedWithTopic = new HashSet<>();
        allSonIds.forEach(i -> {
            List<Integer> id = topicKnowledgeCorrelationDao.selectPointLinkedWithTopic(i);
            pointLinkedWithTopic.addAll(id);
        });
        return pointLinkedWithTopic;
    }

    /**
     * 提交学生错题信息
     *
     * @param studentNumber
     * @param wrongEvents
     * @return void
     * @author Tang
     * @date 2023/9/23 10:27
     **/
    @Async
    public void submitTopicWrong(String studentNumber, List<TopicWrongEvent> wrongEvents) {
        //获取题目id
        List<Integer> topicIds = wrongEvents.stream().map(TopicWrongEvent::getTopicId).collect(Collectors.toList());
        //批量查询题目和知识点的关联关系
        List<TopicKnowledgeCorrelation> topicKnowledgeCorrelationsTemp = topicKnowledgePointDao.selectByTopicIds(topicIds);
        if (topicKnowledgeCorrelationsTemp.isEmpty()) {
            return;
        }
        //查询与题目关联的所有知识点
        Map<Integer, List<TopicKnowledgeCorrelation>> topicKnowledgeCorrelationMap = new HashMap<>();
        for (TopicKnowledgeCorrelation topicKnowledgeCorrelation : topicKnowledgeCorrelationsTemp) {
            List<TopicKnowledgeCorrelation> orDefault = topicKnowledgeCorrelationMap.getOrDefault(topicKnowledgeCorrelation.getTopicId(), new ArrayList<>());
            orDefault.add(topicKnowledgeCorrelation);
            topicKnowledgeCorrelationMap.put(topicKnowledgeCorrelation.getTopicId(), orDefault);
        }
        //重新计算题目与知识点关联
        List<TopicKnowledgeCorrelation> topicKnowledgeCorrelations = new ArrayList<>();
        for (Integer topicId : topicIds) {
            List<TopicKnowledgeCorrelation> knowledgeCorrelations = topicKnowledgeCorrelationMap.get(topicId);
            if (knowledgeCorrelations.isEmpty()) {
                continue;
            }
            for (TopicKnowledgeCorrelation knowledgeCorrelation : knowledgeCorrelations) {
                topicKnowledgeCorrelations.add(TopicKnowledgeCorrelation.clone(knowledgeCorrelation));
            }
        }
        //获取题目id和题目类型、题目记录时间的对应关系
        Map<Integer, Pair<String, LocalDateTime>> topicIdToTypeMap = new HashMap<>();
        for (TopicWrongEvent wrongEvent : wrongEvents) {
            Pair<String, LocalDateTime> pair = new Pair<>(wrongEvent.getTopicType(), wrongEvent.getProblemTime());
            topicIdToTypeMap.put(wrongEvent.getTopicId(), pair);
        }
        //批量插入学生题目数据
        List<TopicUpdate> topicUpdates = topicKnowledgeCorrelations.stream().map(i -> {
            TopicUpdate topicUpdate = new TopicUpdate();
            topicUpdate.setStudentNumber(studentNumber);
            topicUpdate.setTopicId(i.getTopicId());
            //设置题目类型和时间
            String type = "单选题";
            LocalDateTime problemTime = LocalDateTime.now();
            Pair<String, LocalDateTime> pair = topicIdToTypeMap.get(topicUpdate.getTopicId());
            if (pair.getKey() != null && !pair.getKey().isEmpty()) {
                type = pair.getKey();
            }
            if (pair.getValue() != null) {
                problemTime = pair.getValue();
            }
            topicUpdate.setTopicType(type);
            topicUpdate.setUpdateTime(Timestamp.valueOf(problemTime));
            topicUpdate.setUpdateType(TopicUpdateTypeEnum.WRONG.getTypeName());
            return topicUpdate;
        }).collect(Collectors.toList());
        if (!topicUpdates.isEmpty()) {
            topicUpdateDao.insertTopicUpdateBatch(topicUpdates);
        }
        //更新学生错题信息
        List<Integer> pointIds = topicKnowledgeCorrelations.stream().map(TopicKnowledgeCorrelation::getPointId).collect(Collectors.toList());
        if (pointIds.isEmpty()) {
            return;
        }
        List<TopicWrongPointLink> topicWrongPointLink = topicUpdateDao.selectWrongLinkNumByPointIds(studentNumber, pointIds);
        for (TopicKnowledgeCorrelation topicKnowledgeCorrelation : topicKnowledgeCorrelations) {
            Integer pointId = topicKnowledgeCorrelation.getPointId();
            boolean flag = false;
            for (TopicWrongPointLink wrongPointLink : topicWrongPointLink) {
                if (pointId.equals(wrongPointLink.getPointId())) {
                    wrongPointLink.setNum(wrongPointLink.getNum() + 1);
                    flag = true;
                }
            }
            if (!flag) {
                TopicWrongPointLink wrongPointLink = new TopicWrongPointLink();
                wrongPointLink.setNum(1);
                wrongPointLink.setPointId(pointId);
                wrongPointLink.setStudentNumber(studentNumber);
                topicWrongPointLink.add(wrongPointLink);
            }
        }
        //批量更新学生错题数据
        if (!topicWrongPointLink.isEmpty()) {
            topicUpdateDao.insertOrUpdateStudentWrongPointLinkBatch(topicWrongPointLink);
        }
    }

    @Autowired
    public void setTopicUpdateDao(TopicUpdateDao topicUpdateDao) {
        this.topicUpdateDao = topicUpdateDao;
    }

    @Autowired
    public void setTopicKnowledgeCorrelationDao(TopicKnowledgeCorrelationDao topicKnowledgeCorrelationDao) {
        this.topicKnowledgeCorrelationDao = topicKnowledgeCorrelationDao;
    }

    @Autowired
    public void setTopicKnowledgePointDao(TopicKnowledgePointDao topicKnowledgePointDao) {
        this.topicKnowledgePointDao = topicKnowledgePointDao;
    }

    @Autowired
    public void setTopicService(TopicService topicService) {
        this.topicService = topicService;
    }

    @Autowired
    public void setDailyExerciseDao(DailyExerciseDao dailyExerciseDao) {
        this.dailyExerciseDao = dailyExerciseDao;
    }

    @Autowired
    public void setTopicWrongDao(TopicWrongDao topicWrongDao) {
        this.topicWrongDao = topicWrongDao;
    }
}
